package com.security.test.filter;

import cn.hutool.core.util.ObjectUtil;
import com.google.protobuf.ServiceException;
import com.security.test.config.MyUsernamePasswordAuthenticationToken;
import com.security.test.utils.JwtUtil;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Component
public class JwtTokenFilter2 extends GenericFilterBean {
//    SecurityContextPersistenceFilter

    @Autowired
    private JwtUtil jwtUtil;

    //    public JwtAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
//        super(authenticationManager);
//
//    }
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String requestURI = request.getRequestURI();
        String header = request.getHeader("Authorization");
        String key = request.getHeader("key");
        if (key != null) {
            BufferedReader white_list = new BufferedReader(new FileReader("src/main/resources/white_list"));
            String line = null;
            while ((line = white_list.readLine()) != null) {
                System.out.println(line);
                if (key.equals(line)) {
                    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, null);
                    SecurityContextHolder.getContext().setAuthentication(token);
                    chain.doFilter(request, response);
                    return;
                }
            }

        }
        if (ObjectUtil.isEmpty(header) && StringUtils.isEmpty(key)) {
            chain.doFilter(request, response);
            return;
        }

        // 如果token不为空，并且是以指定票据开头
//        if (ObjectUtil.isNotEmpty(header) && header.startsWith(ConstantKey.BEARER)) {
//            // 如果请求路径是放行路径，则直接跳过认证
//            List<String> anonUrlList = Arrays.asList(AuthWhiteList.AUTH_WHITELIST);
//            if (anonUrlList.contains(requestURI)) {
//                chain.doFilter(request, response);
//                return;
//            }
//        }

        MyUsernamePasswordAuthenticationToken authentication = getAuthentication(request, response);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    private MyUsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            long start = System.currentTimeMillis();
            String token = request.getHeader("Authorization");
            if (ObjectUtil.isEmpty(token)) {
                throw new ServiceException("Token不能为空!");
            }
            // parse the token.
            String user = null;
            Claims claims = jwtUtil.parseJWT(token);
//            Claims claims = Jwts.parser().setSigningKey(ConstantKey.SIGNING_KEY).parseClaimsJws(token.replace(ConstantKey.BEARER, "")).getBody();
            // token签发时间
            long issuedAt = claims.getIssuedAt().getTime();
            // 当前时间
            long currentTimeMillis = System.currentTimeMillis();
            // token过期时间
            long expirationTime = claims.getExpiration().getTime();
            // 1. 签发时间 < 当前时间 < (签发时间+((token过期时间-token签发时间)/2)) 不刷新token
            // 2. (签发时间+((token过期时间-token签发时间)/2)) < 当前时间 < token过期时间 刷新token并返回给前端
            // 3. tokne过期时间 < 当前时间 跳转登录，重新登录获取token
            // 验证token时间有效性
            if ((issuedAt + ((expirationTime - issuedAt) / 2)) < currentTimeMillis && currentTimeMillis < expirationTime) {

                // 重新生成token start
//                Calendar calendar = Calendar.getInstance();
//                Date now = calendar.getTime();
                // 设置签发时间
//                calendar.setTime(new Date());
                // 设置过期时间
//                calendar.add(Calendar.MINUTE, 5);// 5分钟
//                Date time = calendar.getTime();
                String refreshToken = jwtUtil.createJWT(claims.getSubject());
//                String refreshToken = Jwts.builder()
//                        .setSubject(claims.getSubject())
//                        .setIssuedAt(now)//签发时间
//                        .setExpiration(time)//过期时间
//                        .signWith(SignatureAlgorithm.HS512, ConstantKey.SIGNING_KEY) //采用什么算法是可以自己选择的，不一定非要采用HS512
//                        .compact();
                // 重新生成token end

                // 主动刷新token，并返回给前端
                response.addHeader("refreshToken", refreshToken);
            }
            long end = System.currentTimeMillis();
            log.info("执行时间: {}", (end - start) + " 毫秒");
//            user = claims.getSubject();
//            if (user != null) {
//                String[] authoritys = user.split("-")[1].split(",");
//                ArrayList<GrantedAuthority> authorities = new ArrayList<>();
//                for (int i = 0; i < authoritys.length; i++) {
//                    String authority = authoritys[i];
//                    // 处理解析权限异常，在注入权限的时候直接用的：用户名+数组，导致字符串中是"admin-[admin,xx1,xx2]"，而在解析的时候没有把两个括号[]去掉，导致权限识别错误，识别成了"[admin"和"xx2]"。
//                    if (i == 0) {
//                        authority = authority.replaceAll("\\[", "");
//                    }
//                    if (i == authoritys.length - 1) {
//                        authority = authority.replaceAll("\\]", "");
//                    }
//                    authorities.add(new GrantedAuthorityImpl(authority));
//                }
//            }
            return new MyUsernamePasswordAuthenticationToken(user, null, null);
        } catch (Exception e) {
            e.printStackTrace();
            response.getWriter().write("非法的token");
            throw new RuntimeException("token 非法");
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        doFilterInternal((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }
}